package newcrunch.grader;

import java.awt.EventQueue;
import java.awt.Point;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import newcrunch.gui.RightTab;
import newcrunch.util.DisplayHelper;
import newcrunch.util.Logging;
import newcrunch.util.Misc;

/**
 *
 * @author ccadm
 */
public class SubmissionsManager 
{
    private String course;
    private String task;
    private String group;
    private List<SubmissionInfo> submissionsCache;
    
    private final GradingPanel gradingPanel;

    public SubmissionsManager(final GradingPanel outer) {
        gradingPanel = outer;
    }

    /**
     * Call this to repeat the previous call to updateSubmissions.
     */
    public void updateSubmissions() {
        updateSubmissions(course, task, group);
    }

    /**
     * Update the grading lists according to the given course and task.
     * @param course
     * @param task
     */
    public void updateSubmissions(String course, String task, String group) {
        this.course = course;
        this.task = task;
        this.group = group;
        try {
            ConnectionManager mgr = Misc.getBaseGUI(gradingPanel).getAuthManager().getConnectionManager();
            List<SubmissionInfo> submissions = mgr.getSubmissionInfo(course, task, group);
            ArrayList<String> gradedTmpList = new ArrayList<String>();
            ArrayList<String> ungradedTmpList = new ArrayList<String>();
            submissionsCache = new ArrayList<SubmissionInfo>();
            for (SubmissionInfo sub : submissions) {
                if (sub.dateGraded == null || "".equals(sub.dateGraded)) {
                    ungradedTmpList.add(sub.username);
                } else {
                    gradedTmpList.add(sub.username);
                }
                SubmissionInfo curr = new SubmissionInfo(sub.submissionId, sub.username, sub.firstname, sub.dateGraded, sub.filename, sub.comment, sub.grader, sub.dateSubmitted);
                submissionsCache.add(curr);
            }
            //add the students that did not submit
            ArrayList<String> noSubmissionsList = getNoSubmissions(ungradedTmpList, gradedTmpList, new ArrayList<String>(mgr.getStudents(group)));
            int oldSize = ungradedTmpList.size();
            for (int i = 0; i < noSubmissionsList.size(); i++) {
                String username = noSubmissionsList.get(i);
                ungradedTmpList.add(username);
                submissionsCache.add(new SubmissionInfo(null, username, mgr.getName(username), null, null, null, null, null));
            }
            //update the lists to display
            gradingPanel.getUngradedList().setModel(new GradingListModel(ungradedTmpList, submissionsCache));
            gradingPanel.getGradedList().setModel(new GradingListModel(gradedTmpList, submissionsCache));
            //disable all users whose submissions are not found
            for (int i = 0; i < noSubmissionsList.size(); i++) {
                gradingPanel.getUngradedList().getGradingListModel().setDisabled(oldSize + i);
            }
            //set the count in the tab titles
            gradingPanel.getTabbedPane().setTitleAt(0, "Ungraded (" + String.valueOf(ungradedTmpList.size()) + ")");
            gradingPanel.getTabbedPane().setTitleAt(1, "Graded (" + String.valueOf(gradedTmpList.size()) + ")");
            //sort according to username
            //must retain this for many functions to work!
            Collections.sort(submissionsCache, new UsernameComparator());
            //refreshes the grading lists.
            EventQueue.invokeLater(new Runnable() {
                public void run() {
                    gradingPanel.repaint();
                }
            });
        }
        catch (UnsupportedEncodingException exp) {
            Logging.log(exp);
        }
        catch (IOException e) {
            Logging.log(e);
            DisplayHelper.showErrorFrame(gradingPanel, "Connection Error", "Unable to connect to server.");
        }
    }

    /**
     * Find the students who did not submit their lab.
     * @param ungraded
     * @param graded
     * @param total
     * @return
     */
    private ArrayList<String> getNoSubmissions(ArrayList<String> ungraded, ArrayList<String> graded, ArrayList<String> total) {
        ArrayList<String> results = new ArrayList<String>();
        int ungradedCounter = 0;
        int gradedCounter = 0;
        int totalCounter;
        for (totalCounter = 0; totalCounter < total.size() && ungradedCounter < ungraded.size() && gradedCounter < graded.size(); totalCounter++) {
            String curr = total.get(totalCounter);
            if (ungraded.get(ungradedCounter).equals(curr)) {
                ungradedCounter++;
            } else if (graded.get(gradedCounter).equals(curr)) {
                gradedCounter++;
            } else {
                results.add(curr);
            }
        }
        while (ungradedCounter < ungraded.size() && totalCounter < total.size()) {
            if (ungraded.get(ungradedCounter).equals(total.get(totalCounter))) {
                ungradedCounter++;
            } else {
                results.add(total.get(totalCounter));
            }
            totalCounter++;
        }
        while (gradedCounter < graded.size() && totalCounter < total.size()) {
            if (graded.get(gradedCounter).equals(total.get(totalCounter))) {
                gradedCounter++;
            } else {
                results.add(total.get(totalCounter));
            }
            totalCounter++;
        }
        while (totalCounter < total.size()) {
            results.add(total.get(totalCounter));
            totalCounter++;
        }
        return results;
    }

    SubmissionInfo getSubmissionInfo(String username) {
        int index = Collections.binarySearch(submissionsCache, new SubmissionInfo(username), new UsernameComparator());
        if (index >= 0) {
            return submissionsCache.get(index);
        } else {
            return null;
        }
    }

    private String formatCompilerOutput(String sid) throws UnsupportedEncodingException, IOException {
        StringBuilder result = new StringBuilder();
        ConnectionManager mgr = Misc.getBaseGUI(gradingPanel).getAuthManager().getConnectionManager();
        int total = mgr.getTotalCases(sid);
        int passed = mgr.getCorrectCases(sid);
        String output = mgr.getCompilerOutput(sid);
        output = output.replaceAll("<br/>", "\n");
        result.append("Test Cases: " + passed + "/" + total + " correct.\n");
        result.append(output);
        return result.toString();
    }

    String getUsername(String text) {
        int spacePosition = text.indexOf(" ");
        if (spacePosition >= 0) {
            return text.substring(0, spacePosition);
        } else {
            return text;
        }
    }

    void loadSubmission(GradingList list, Point point) {
        //Get the submission id from the given username
        int index = list.locationToIndex(point);
        String username = list.getGradingListModel().getElementAt(index).toString();
        if (list.getGradingListModel().isDisabled(index)) {
            return;
        }
        username = getUsername(username);
        SubmissionInfo submissionInfo = getSubmissionInfo(username);
        //check whether that submission is already opened inside the editor
        if (gradingPanel.getRightPanel().hasSubmission(submissionInfo)) {
            gradingPanel.getRightPanel().focusTab(submissionInfo);
        } else {
            try {
                ConnectionManager ga = Misc.getBaseGUI(gradingPanel).getAuthManager().getConnectionManager();
                String result = "";
                if (list == gradingPanel.getGradedList()) {
                    result = ga.getGradedFile(submissionInfo.submissionId);
                } else if (list == gradingPanel.getUngradedList()) {
                    result = ga.getSubmittedFile(submissionInfo.submissionId);
                }
                
                //separated
                submissionInfo.comment = ga.getComment(submissionInfo.submissionId);
                
                final RightTab newTab = gradingPanel.getRightPanel().addSubmission(submissionInfo, result, submissionInfo.comment, formatCompilerOutput(submissionInfo.submissionId));
                newTab.addPropertyChangeListener(new PropertyChangeListener() {

                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {
                        String propName = evt.getPropertyName();
                        if ("selectInPanel".equals(propName))
                            gradingPanel.selectInPanel((SubmissionInfo) evt.getNewValue());
                        
                        else if ("updateSubmissions".equals(propName))
                            updateSubmissions();
                        
                        else if ("updateSubmissionInfo".equals(propName))
                        {
                            SubmissionInfo oldInfo = newTab.getSubmissionInfo();
                                for (SubmissionInfo info : submissionsCache)
                                    if (info.equals(oldInfo))
                                        newTab.setSubmissionInfo(info);
                        }
                    }
                });
                if (list == gradingPanel.getGradedList()) {
                    newTab.setMarks(ga.getMarks(submissionInfo.submissionId));
                    newTab.firePropertyChange("setOldText", null, getStudentSubmission(submissionInfo.submissionId));
                } else {
                    newTab.firePropertyChange("setOldText", null, result.toString());
                }
            }
            catch (IOException e) {
                Logging.log(e);
                DisplayHelper.showErrorFrame(gradingPanel, "Connection Error", "Unable to connect to server.");
            }
        }
    }

    private String getStudentSubmission(String sid) {
        try {
            ConnectionManager mgr = Misc.getBaseGUI(gradingPanel).getAuthManager().getConnectionManager();
            return mgr.getSubmittedFile(sid);
        }
        catch (UnsupportedEncodingException e) {
            Logging.log(e);
        }
        catch (IOException e) {
            DisplayHelper.showErrorFrame(gradingPanel, "Connection error", "Unable to connect to server.");
        }
        return null;
    }

}
